/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://gitee.com/newgolo/embedme.git
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include "random.h"
#include "libemb/Tracer.h"
#include "libemb/StrUtil.h"
#include "libembx/MqttClient.h"

namespace libembx{
using namespace libemb;

MqttClientProxy::MqttClientProxy()
{
}

MqttClientProxy::~MqttClientProxy()
{
}

void MqttClientProxy::onRecvMsg(const std::string& topic, const std::string& msg)
{
	TRACE_INFO_CLASS("receive topic: %s, size(%d), msg[%s]\n",CSTR(topic),msg.size(),CSTR(msg));
}

bool MqttClientProxy::keepAlive()
{
	int ret = mqtt_keep_alive(m_client);
	if (ret!=0)
	{
		TRACE_ERR_CLASS("mqtt keep alive error:%d\n",ret);
		return false;
	}
	return true;
}

bool MqttClientProxy::setWillOption(const std::string& topic,const std::string& msg, int qos, bool retained)
{
	int ret = mqtt_set_will_options(m_client,(char*)CSTR(topic),(mqtt_qos_t)qos,(uint8)(retained?1:0),(char*)CSTR(msg));
	if (ret!=0)
	{
		TRACE_ERR_CLASS("mqtt set will options:%d\n",ret);
		return false;
	}
	return true;
}

bool MqttClientProxy::open(std::string clientID, int maxMsgSize, std::string username, std::string password)
{
	if (m_client!=nullptr)
	{
		return true;
	}
	m_client = mqtt_lease();
	if (m_client==nullptr)
	{
		TRACE_ERR_CLASS("mqtt open error!\n");
		return false;
	}
	
	m_clientID = clientID;
	m_username = username;
	m_password = password;

	if (maxMsgSize>0)
	{
		/* 默认是1024字节 */
		mqtt_set_read_buf_size(m_client,maxMsgSize+68);/* PUBLISH包:2(固定包头)+2(可变包头)+64(topic)+payload */
		mqtt_set_write_buf_size(m_client,maxMsgSize+68);
	}
	mqtt_set_cmd_timeout(m_client,4000);/* 默认命令超时时间:4000ms */

	if (m_clientID.empty())
	{
		m_clientID = std::string(random_string(10));
	}
	mqtt_set_client_id(m_client,(char*)CSTR(m_clientID));
	
	if (m_username.empty())
	{
		m_username = std::string(random_string(10));
	}
	mqtt_set_user_name(m_client,(char*)CSTR(m_username));
	
	if (m_password.empty())
	{
		m_password = std::string(random_string(10));
	}
	mqtt_set_password(m_client,(char*)CSTR(m_password));
	
	return true;
}

bool MqttClientProxy::connect(const std::string& server,std::string certificate)
{	
	if (server.empty())
	{
		TRACE_ERR_CLASS("not specify a server!\n");
		return false;
	}
	auto strVect = StrUtil::splitString(server, ":", false);
	if (strVect.size()!=2)
	{
		TRACE_ERR_CLASS("server url error:[%s]!\n",CSTR(server));
		return false;
	}
	m_server = strVect[0];
	m_port = strVect[1];
	mqtt_set_host(m_client, (char*)CSTR(m_server));
	mqtt_set_port(m_client, (char*)CSTR(m_port));

	if(!certificate.empty())
	{
		m_certificate = certificate;
		mqtt_set_ca(m_client,(char*)CSTR(m_certificate));
	}

	int ret = mqtt_connect(m_client);
	if(ret!=0)
	{
		TRACE_ERR_CLASS("mqtt connect error:%d!\n",ret);
		return false;
	}
	return true;
}

bool MqttClientProxy::subscribe(const std::string& topic,int qos,MqttMsgHandler handler)
{
	if (topic.empty())
	{
		TRACE_ERR_CLASS("param error,topic:%s\n",CSTR(topic));
		return false;
	}
	int ret = mqtt_subscribe(m_client, CSTR(topic), (mqtt_qos_t)qos, handler);
	if (ret!=0)
	{
		TRACE_ERR_CLASS("mqtt subscribe error:%d, topic:%s, qos:%d\n",ret,CSTR(topic),qos);
		return false;
	}
	return true;
}

bool MqttClientProxy::publish(const std::string& topic,const std::string& msg, int qos)
{
	if (topic.empty() || msg.empty())
	{
		TRACE_ERR_CLASS("param error,topic:%s, msg:%s\n",CSTR(topic),CSTR(msg));
		return false;
	}
	mqtt_message_t mqttMsg;
	memset(&mqttMsg,0,sizeof(mqttMsg));
	mqttMsg.payload = (void *)msg.data();
	mqttMsg.payloadlen = msg.size();
	mqttMsg.qos = (mqtt_qos_t)qos;
	int ret = mqtt_publish(m_client, CSTR(topic), &mqttMsg);
	if (ret!=0)
	{
		TRACE_ERR_CLASS("mqtt publish error:%d, topic:%s, msg(%d)[%02x %02x %02x %02x ...]\n",ret,CSTR(topic),msg.size(),msg[0],msg[1],msg[2],msg[3]);
		return false;
	}
	return true;
}

bool MqttClientProxy::unsubscribe(const std::string& topic)
{
	if (topic.empty())
	{
		TRACE_ERR_CLASS("param error,topic:%s\n",CSTR(topic));
		return false;
	}
	int ret = mqtt_unsubscribe(m_client, CSTR(topic));
	if (ret!=0)
	{
		TRACE_ERR_CLASS("mqtt unsubscribe error:%d, topic:%s\n",ret,CSTR(topic));
		return false;
	}
	return true;
}


bool MqttClientProxy::disconnect()
{
	int ret = mqtt_disconnect(m_client);
	if (ret!=0)
	{
		TRACE_ERR_CLASS("mqtt disconnect error:%d\n",ret);
		return false;
	}
	return true;
}

bool MqttClientProxy::close()
{
	int ret = mqtt_release(m_client);
	if (ret!=0)
	{
		TRACE_ERR_CLASS("mqtt release error:%d\n",ret);
		return false;
	}
	m_client=nullptr;
	return true;
}

}

